package com.kexilo.core.common.aspect;

import com.kexilo.core.common.annotation.CircuitBreaker;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 熔断器切面处理器
 *
 * @author Kexilo
 */
@Aspect
@Component
public class CircuitBreakerAspect {
    
    private static final Logger log = LoggerFactory.getLogger(CircuitBreakerAspect.class);
    
    /** 熔断器状态管理 */
    private final ConcurrentHashMap<String, CircuitBreakerState> circuitBreakers = new ConcurrentHashMap<>();

    @Around("@annotation(circuitBreaker)")
    public Object doCircuitBreaker(ProceedingJoinPoint joinPoint, CircuitBreaker circuitBreaker) throws Throwable {
        String circuitBreakerName = getCircuitBreakerName(joinPoint, circuitBreaker);
        CircuitBreakerState state = circuitBreakers.computeIfAbsent(circuitBreakerName, 
                k -> new CircuitBreakerState(circuitBreaker));
        
        // 检查熔断器状态
        switch (state.getState()) {
            case OPEN:
                return handleOpenState(joinPoint, circuitBreaker, state);
                
            case HALF_OPEN:
                return handleHalfOpenState(joinPoint, circuitBreaker, state);
                
            case CLOSED:
            default:
                return handleClosedState(joinPoint, circuitBreaker, state);
        }
    }

    /**
     * 处理关闭状态
     */
    private Object handleClosedState(ProceedingJoinPoint joinPoint, CircuitBreaker circuitBreaker, 
                                   CircuitBreakerState state) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            
            // 记录成功调用
            long duration = System.currentTimeMillis() - startTime;
            state.recordSuccess(duration, circuitBreaker.slowCallDurationThreshold());
            
            return result;
            
        } catch (Exception e) {
            // 记录失败调用
            if (shouldRecordException(e, circuitBreaker)) {
                state.recordFailure();
                
                // 检查是否需要开启熔断器
                if (state.shouldOpen(circuitBreaker)) {
                    state.open();
                    log.warn("熔断器 {} 开启，失败率: {}%", state.getName(), state.getFailureRate());
                }
            }
            
            throw e;
        }
    }

    /**
     * 处理半开状态
     */
    private Object handleHalfOpenState(ProceedingJoinPoint joinPoint, CircuitBreaker circuitBreaker, 
                                     CircuitBreakerState state) throws Throwable {
        // 检查是否允许调用
        if (!state.isCallPermittedInHalfOpen(circuitBreaker.permittedNumberOfCallsInHalfOpenState())) {
            return executeFallback(joinPoint, circuitBreaker, 
                    new RuntimeException("熔断器半开状态，调用被拒绝"));
        }
        
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            
            // 记录成功调用
            long duration = System.currentTimeMillis() - startTime;
            state.recordSuccess(duration, circuitBreaker.slowCallDurationThreshold());
            
            // 检查是否可以关闭熔断器
            if (state.shouldClose(circuitBreaker)) {
                state.close();
                log.info("熔断器 {} 关闭，恢复正常", state.getName());
            }
            
            return result;
            
        } catch (Exception e) {
            if (shouldRecordException(e, circuitBreaker)) {
                state.recordFailure();
                state.open(); // 重新开启
                log.warn("熔断器 {} 半开状态失败，重新开启", state.getName());
            }
            throw e;
        }
    }

    /**
     * 处理开启状态
     */
    private Object handleOpenState(ProceedingJoinPoint joinPoint, CircuitBreaker circuitBreaker, 
                                 CircuitBreakerState state) throws Throwable {
        // 检查是否可以转为半开状态
        if (state.canTransitionToHalfOpen(circuitBreaker.waitDurationInOpenState() * 1000)) {
            state.halfOpen();
            log.info("熔断器 {} 转为半开状态", state.getName());
            return handleHalfOpenState(joinPoint, circuitBreaker, state);
        }
        
        // 执行降级逻辑
        return executeFallback(joinPoint, circuitBreaker, 
                new RuntimeException("熔断器开启，调用被拒绝"));
    }

    /**
     * 执行降级方法
     */
    private Object executeFallback(ProceedingJoinPoint joinPoint, CircuitBreaker circuitBreaker, 
                                 Exception originalException) throws Throwable {
        if (circuitBreaker.fallbackMethod().isEmpty()) {
            throw originalException;
        }
        
        try {
            Object target = joinPoint.getTarget();
            Method fallbackMethod = target.getClass().getDeclaredMethod(circuitBreaker.fallbackMethod(), 
                    ((MethodSignature) joinPoint.getSignature()).getParameterTypes());
            fallbackMethod.setAccessible(true);
            
            return fallbackMethod.invoke(target, joinPoint.getArgs());
            
        } catch (Exception e) {
            log.error("降级方法执行失败: {}", circuitBreaker.fallbackMethod(), e);
            throw originalException;
        }
    }

    /**
     * 判断异常是否需要记录
     */
    private boolean shouldRecordException(Exception e, CircuitBreaker circuitBreaker) {
        // 检查忽略的异常类型
        for (Class<? extends Throwable> ignoreException : circuitBreaker.ignoreExceptions()) {
            if (ignoreException.isAssignableFrom(e.getClass())) {
                return false;
            }
        }
        
        // 检查记录的异常类型
        for (Class<? extends Throwable> recordException : circuitBreaker.recordExceptions()) {
            if (recordException.isAssignableFrom(e.getClass())) {
                return true;
            }
        }
        
        return false;
    }

    /**
     * 获取熔断器名称
     */
    private String getCircuitBreakerName(ProceedingJoinPoint joinPoint, CircuitBreaker circuitBreaker) {
        if (!circuitBreaker.name().isEmpty()) {
            return circuitBreaker.name();
        }
        
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getDeclaringType().getSimpleName() + "#" + signature.getName();
    }

    /**
     * 熔断器状态枚举
     */
    public enum State {
        CLOSED, OPEN, HALF_OPEN
    }

    /**
     * 熔断器状态管理类
     */
    public static class CircuitBreakerState {
        private final String name;
        private volatile State state = State.CLOSED;
        private final AtomicInteger totalCalls = new AtomicInteger(0);
        private final AtomicInteger failedCalls = new AtomicInteger(0);
        private final AtomicInteger slowCalls = new AtomicInteger(0);
        private final AtomicInteger halfOpenCalls = new AtomicInteger(0);
        private final AtomicLong lastFailureTime = new AtomicLong(0);
        
        public CircuitBreakerState(CircuitBreaker config) {
            this.name = "CircuitBreaker";
        }
        
        public String getName() {
            return name;
        }
        
        public State getState() {
            return state;
        }
        
        public void recordSuccess(long duration, long slowCallThreshold) {
            totalCalls.incrementAndGet();
            if (duration > slowCallThreshold) {
                slowCalls.incrementAndGet();
            }
            
            // 重置滑动窗口（简化实现）
            if (totalCalls.get() > 100) {
                totalCalls.set(50);
                failedCalls.set(failedCalls.get() / 2);
                slowCalls.set(slowCalls.get() / 2);
            }
        }
        
        public void recordFailure() {
            totalCalls.incrementAndGet();
            failedCalls.incrementAndGet();
            lastFailureTime.set(System.currentTimeMillis());
        }
        
        public boolean shouldOpen(CircuitBreaker config) {
            int total = totalCalls.get();
            int failed = failedCalls.get();
            
            if (total < config.minimumNumberOfCalls()) {
                return false;
            }
            
            double failureRate = (double) failed / total * 100;
            return failureRate >= config.failureRateThreshold();
        }
        
        public boolean shouldClose(CircuitBreaker config) {
            int halfOpenCallsCount = halfOpenCalls.get();
            return halfOpenCallsCount >= config.permittedNumberOfCallsInHalfOpenState();
        }
        
        public void open() {
            this.state = State.OPEN;
            this.lastFailureTime.set(System.currentTimeMillis());
        }
        
        public void close() {
            this.state = State.CLOSED;
            this.halfOpenCalls.set(0);
        }
        
        public void halfOpen() {
            this.state = State.HALF_OPEN;
            this.halfOpenCalls.set(0);
        }
        
        public boolean canTransitionToHalfOpen(long waitDuration) {
            return state == State.OPEN && 
                   (System.currentTimeMillis() - lastFailureTime.get()) > waitDuration;
        }
        
        public boolean isCallPermittedInHalfOpen(int permittedCalls) {
            return halfOpenCalls.incrementAndGet() <= permittedCalls;
        }
        
        public double getFailureRate() {
            int total = totalCalls.get();
            if (total == 0) return 0.0;
            return (double) failedCalls.get() / total * 100;
        }
    }
}
